package com.yuan.yblog.security.component;

import com.yuan.yblog.common.consts.RedisKey;
import com.yuan.yblog.common.service.EntityCacheService;
import com.yuan.yblog.security.config.IgnoreUrlsConfig;
import com.yuan.yblog.security.pojo.LoginUser;
import com.yuan.yblog.security.util.JwtTokenUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;

/**
 * jwt校验 过滤器
 */
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Autowired
    EntityCacheService entityCacheService;

    @Autowired
    IgnoreUrlsConfig ignoreUrlsConfig;

    AntPathMatcher pathMatcher = new AntPathMatcher();

    @Value("${jwt.tokenHeader}")
    private String tokenHeader;
    @Value("${jwt.tokenHead}")
    private String tokenHead;
    @Value("${jwt.expiration}")
    private Long expiration;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 不在匿名路径才检验
        if(!isIgnoredUrl(request)) {
            //http请求头key = Authorization 的内容（即value）
            String authHeader = request.getHeader(this.tokenHeader);
            // 校验token是否存在
            if (authHeader != null && authHeader.startsWith(this.tokenHead)) {
                // 截取"Bearer "后面的jwt串
                String token = authHeader.substring(this.tokenHead.length());
                // 解析token，获取用户标识（比如邮箱）
                String userIdentity = String.valueOf(jwtTokenUtil.getUserIdentityByToken(token));
                if (userIdentity != null
                        && SecurityContextHolder.getContext().getAuthentication() == null
                ) {
                    // 从缓存中获取登录的用户，若不存在，则表示用户未登录/登录过期
                    LoginUser loginUser = (LoginUser) entityCacheService.getEntity(RedisKey.AUTH_LOGIN_USER, userIdentity, expiration);
                    // 进一步校验jwt是否过期，登录token是否发生变化
                    if (jwtTokenUtil.tokenIsValidate(token, loginUser)
                            && token.equals(loginUser.getToken())
                    ) {
                        // UsernamePasswordAuthenticationToken实现了Authentication接口
                        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
                        authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        // 存入上下文
                        SecurityContextHolder.getContext().setAuthentication(authentication);
                    }

                }
            }
        }
        //放行：由下一个过滤器来操作
        filterChain.doFilter(request, response);
    }

    /**
     * 判断当前请求url是否为匿名路径
     * @param request 请求对象
     */
    private boolean isIgnoredUrl (HttpServletRequest request) {
        // 请求路径
        String requestUrl = request.getRequestURI();
        // 匿名路径
        List<String> ignoreUrls = ignoreUrlsConfig.getUrls();
        for (String ignoreUrl : ignoreUrls) {
            if(pathMatcher.match(ignoreUrl, requestUrl)) {
                return true;
            }
        }
        return false;
    }
}
